﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Kinect;

namespace KinectServer2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private InteropBitmapHelper depthImageHelper;
        private Socket depthSocket;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            depthSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            depthSocket.Bind(new IPEndPoint(IPAddress.Any, 20161));

            UpdateDepthImage();
        }

        private const int ImageWidth = 160, ImageHeight = 120;

        public async void UpdateDepthImage()
        {
            await Task.Run(() =>
                {
                    try
                    {
                        while (depthSocket.Available < 4) Task.Delay(10);

                        byte[] tmp = new byte[4];

                        //if (depthSocket.Available > 4)
                            //return;

                        depthSocket.Receive(tmp, 4, SocketFlags.None);

                        int compressedBytes = BitConverter.ToInt32(tmp, 0);
                        byte[] data = new byte[compressedBytes];

                        while (depthSocket.Available < compressedBytes) Task.Delay(10);

                        depthSocket.Receive(data, 0, compressedBytes, SocketFlags.None);

                        byte[] convertedDepthBits = convertDepthFrame(decompress(data));

                        Dispatcher.Invoke(new Action(() =>
                           {
                               if (depthImageHelper == null)
                               {
                                   depthImageHelper = new InteropBitmapHelper(ImageWidth, ImageHeight, convertedDepthBits);
                                   imgDepth.Source = depthImageHelper.InteropBitmap;
                               }
                               else
                               {
                                   depthImageHelper.UpdateBits(convertedDepthBits);
                               }
                           }));
                    }
                    catch { }
                });

            UpdateDepthImage();
        }


        private byte[] depthFrame32 = new byte[ImageWidth * ImageHeight * 4];
        private const int RedIndex = 2;
        private const int GreenIndex = 1;
        private const int BlueIndex = 0;

        int maxIntensity = int.MinValue;
        int minIntensity = int.MaxValue;

        // Converts a 16-bit grayscale depth frame into an 8-bit greyscale frame
        private byte[] convertDepthFrame(byte[] depthFrame)
        {
            int tooNearDepth = 0;
            int tooFarDepth = 255;

            for (int i = 0, i32 = 0; i < depthFrame.Length && i32 < depthFrame32.Length; i += 1, i32 += 4)
            {
                byte intensity = depthFrame[i];

                // transform 13-bit depth information into an 8-bit intensity appropriate
                // for display (we disregard information in most significant bit)
                //byte intensity = (byte)(~(realDepth >> 4));

                if (intensity == tooNearDepth)
                {
                    // dark brown
                    this.depthFrame32[i32 + RedIndex] = 66;
                    this.depthFrame32[i32 + GreenIndex] = 66;
                    this.depthFrame32[i32 + BlueIndex] = 33;
                }
                else if (intensity == tooFarDepth)
                {
                    // dark purple
                    this.depthFrame32[i32 + RedIndex] = 66;
                    this.depthFrame32[i32 + GreenIndex] = 0;
                    this.depthFrame32[i32 + BlueIndex] = 66;
                }
                else
                {
                    // tint the intensity by dividing by per-player values
                    this.depthFrame32[i32 + RedIndex] = (byte)(intensity);
                    this.depthFrame32[i32 + GreenIndex] = (byte)(intensity);
                    this.depthFrame32[i32 + BlueIndex] = (byte)(intensity);
                }
            }

            return depthFrame32;
        }

        private static MemoryStream ms;

        public static byte[] decompress(byte[] gzBuffer)
        {
            ms = new MemoryStream();
            int msgLength = BitConverter.ToInt32(gzBuffer, 0);
            ms.Write(gzBuffer, 4, gzBuffer.Length - 4);

            //byte[] buffer = new byte[msgLength];
            gzBuffer = new byte[msgLength];

            ms.Position = 0;
            GZipStream zip = new GZipStream(ms, CompressionMode.Decompress);
            zip.Read(gzBuffer, 0, msgLength);

            return gzBuffer;
        }
    }
}
